// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Copyright 2011 Paul R. Dixon  dixonp@furui.cs.titech.ac.jp
// Based on Standard Weight from OpenFst

#include <fst/float-weight.h>

namespace fst
{
	// real  semiring: +, x, 0, 1)	
	template <class T>
	class RealWeightTpl : public FloatWeightTpl<T> 	{
	public:
		using FloatWeightTpl<T>::Value;

		typedef RealWeightTpl ReverseWeight; 

		RealWeightTpl() : FloatWeightTpl<T>() { }

		RealWeightTpl(T f) : FloatWeightTpl<T>(f) { }

		RealWeightTpl(const RealWeightTpl<T> &w) : FloatWeightTpl<T>(w) { }

		static const RealWeightTpl<T> Zero() {
			return RealWeightTpl<T>(0.0f);
		}

		static const RealWeightTpl<T> One() {
			return RealWeightTpl<T>(1.0F);
		}

		static const string &Type() {
			static const string type = "real" + FloatWeightTpl<T>::GetPrecisionString();
			return type;
		}

		bool Member() const	{
			return Value() == Value() && Value() != FloatLimits<T>::kNegInfinity;
		}

		RealWeightTpl<T> Quantize(T delta = kDelta) const {
			if (Value() == FloatLimits<T>::kNegInfinity || 
					Value() == FloatLimits<T>::kPosInfinity || Value() != Value())
				return *this;
			else
				return RealWeightTpl<T>(floor(Value() / delta + 0.5F) * delta);
		}

		RealWeightTpl<T> Reverse() const { 
			return *this;
		}

		static uint64 Properties() {
			return	 kLeftSemiring | kRightSemiring | kCommutative;
		}
	};
	

	template <class T>
	inline RealWeightTpl<T> Plus(const RealWeightTpl<T> &w1,
															 const RealWeightTpl<T> &w2) {
		T f1 = w1.Value();
		T f2 = w2.Value();

		if (f1 == FloatLimits<T>::kPosInfinity)
			return w1;
		else if (f2 == FloatLimits<T>::kPosInfinity)
			return w2;
		else
			return RealWeightTpl<T>(f1 + f2);
	}

	inline RealWeightTpl<float> Plus(const RealWeightTpl<float> &w1,
																	 const RealWeightTpl<float> &w2) {
		return Plus<float>(w1, w2);
	}

	inline RealWeightTpl<double> Plus(const RealWeightTpl<double> &w1,
																		const RealWeightTpl<double> &w2) {
		return Plus<double>(w1, w2);
	}

	template <class T>
	inline RealWeightTpl<T> Times(const RealWeightTpl<T> &w1,
																const RealWeightTpl<T> &w2)	{
		T f1 = w1.Value();
		T f2 = w2.Value();
		if (f1 == FloatLimits<T>::kPosInfinity)
			return w1;
		else if (f2 == FloatLimits<T>::kPosInfinity)
			return w2;
		else
			return RealWeightTpl<T>(f1 * f2);
	}

	inline RealWeightTpl<float> Times(const RealWeightTpl<float> &w1,
																		const RealWeightTpl<float> &w2)	{
			return Times<float>(w1, w2);
	}

	inline RealWeightTpl<double> Times(const RealWeightTpl<double> &w1,
																		 const RealWeightTpl<double> &w2)	{
			return Times<double>(w1, w2);
	}

	template <class T>
	inline RealWeightTpl<T> Divide(const RealWeightTpl<T> &w1,
																 const RealWeightTpl<T> &w2,
																 DivideType typ = DIVIDE_ANY)	{
			T f1 = w1.Value();
			T f2 = w2.Value();
			if (f2 == FloatLimits<T>::kPosInfinity)
				return RealWeightTpl<T>::Zero();
			else if (f1 == FloatLimits<T>::kPosInfinity)
				return FloatLimits<T>::kPosInfinity;
			else
				return RealWeightTpl<T>(f1/f2);
	}

	inline RealWeightTpl<float> Divide(const RealWeightTpl<float> &w1,
																		 const RealWeightTpl<float> &w2,
																		 DivideType typ = DIVIDE_ANY)	{
			return Divide<float>(w1, w2, typ);
	}

	inline RealWeightTpl<double> Divide(const RealWeightTpl<double> &w1,
																			const RealWeightTpl<double> &w2,
																			DivideType typ = DIVIDE_ANY) {
			return Divide<double>(w1, w2, typ);
	}

	typedef RealWeightTpl<float> RealWeight;
	typedef RealWeightTpl<double> RealWeightD;
}
